# coding=utf8
__author__ = 'lijian'
import threading
import logging
import time
import struct

import serial
import config

from setting import PLC_SERIAL
import protocol.term
from utils import byteUtil



class Monitor(threading.Thread):
    instance = None
    mutex = threading.Lock()

    @staticmethod
    def GetInstance():
        if (Monitor.instance == None):
            Monitor.mutex.acquire()
            if (Monitor.instance == None):
                Monitor.instance = PlcMonitor()
            Monitor.mutex.release()

        return Monitor.instance

    def __init__(self):
        super(Monitor, self).__init__()
        self._readySendPacket = None  # 等待发送的数据包
        self._readyRecvPack = None  # 已接收待处理的数据包
        self._lastSendPacket = None  # 最后一个发送的数据包
        self._lastSendTime = 0  # 数据包发送时间
        self._event = threading.Event()

        self._forceIdle = False
        self._lastSuspendTime = 0

        self._decoder = protocol.term.PacketDecode()

        self._plcLock=threading.RLock()

    def reset(self):
        pass

    def forceIdle(self):
        self._forcedIdle = True

        self._lastSuspendTime = time.time()
        self._forcedIdle = False

        return True

    def send(self, packet, dest=None, retry=True):
        self._plcLock.acquire()
        if dest:
            packet.uid = dest

        retryTimes = config.plc["retry"] if retry else 1
        response = None
        for r in range(1, retryTimes + 1):
            logging.debug("try send to plc,retry:%d" % r)
            self._readySendPacket = packet
            self._event.wait(config.plc["timeout"])
            if self._event.isSet():
                logging.debug("recv packet ok")
                response = self._readyRecvPack
            # 等待接收超时，复位等待标志
            else:
                logging.debug("wait plc response timeout")

            self._lastSendPacket = None
            self._readySendPacket = None
            self._event.clear()

            if response:
                import lamp.handler

                lamp.handler.response_handler(response)
                self._plcLock.release()
                return response

        self._plcLock.release()
        return None

    def run(self):
        while True:
            if not self._lastSendPacket and self._readySendPacket:  # 有数据包待发送
                logging.debug("send packet to plc:%s" % self._readySendPacket.info())
                self.sendBytes(self._readySendPacket.pack())
                self._lastSendPacket = self._readySendPacket
                self._readySendPacket = None
                self._lastSendTime = time.time()

            # 接收数据包
            raw = self.readBytes()
            if len(raw) > 0:
                remain = struct.unpack('B' * len(raw), raw)
                packet = self._decoder.decode(remain)
                if packet:
                    logging.debug("recv plc packet:%r" % packet.info())

                    # print "wait:",self._lastSendPacket.uid
                    # print "recv:",packet.uid
                    if self._lastSendPacket and self._lastSendPacket.ctrl == packet._ctrl and byteUtil.compareList(
                            self._lastSendPacket.uid, packet.uid):
                        self._event.set()
                        self._readyRecvPack = packet

    def getSuspendTime(self):
        return self._lastSuspendTime

    def sendBytes(self, raw):
        pass

    def readBytes(self):
        pass


class PlcMonitor(Monitor):
    def __init__(self):
        super(PlcMonitor, self).__init__()

    def reset(self):
        self._serial = serial.Serial(port=PLC_SERIAL['port'], baudrate=PLC_SERIAL['baudrate'],
                                     stopbits=PLC_SERIAL['stopbits'], parity=PLC_SERIAL['parity'],timeout=0.1)
        # self._serial.setTimeout(0.1)

        inited = False
        count = 0
        while not inited:
            packet = protocol.term.Packet(ctrl=0x23, uid=config.info["boxNo"])

            logging.info("set concent plc region,%s" % packet.info())
            self._serial.write(packet.pack())

            count += 1
            raw = self._serial.read(1024)
            if len(raw) > 0:
                remain = struct.unpack('B' * len(raw), raw)
                packet = self._decoder.decode(remain)
                if packet:
                    logging.debug("recv plc packet:%r" % packet.info())
                    logging.debug("init plc successful")
                    return True

    def sendBytes(self, raw):
        self._serial.write(raw)

    def readBytes(self):
        raw = self._serial.read(1024)
        return raw


def startMonitor():
    th = Monitor.GetInstance()
    th.setDaemon(True)
    th.reset()
    th.start()